import { createProcessingFromAsyncFunc, HasOwner, step } from "xbsj-base";
import { RotationEditing, RotatorPlaneType } from ".";
import { getSceneScaleForScreenPixelSize } from "@czmSrc/utils";
import * as Cesium from 'cesium';
import { ESCesiumViewer } from "@czmSrc/ESCesiumViewer";
type RotatorStartInfoType = {
    currentPlaneType: RotatorPlaneType;
    currentD2c: number;
    currentV: Cesium.Cartesian3 | undefined;
    startRotation: number;
    pointerId: number;
};
export class RotatorRunning extends HasOwner<RotationEditing> {
    constructor(owner: RotationEditing) {
        super(owner);
        const { czmViewer } = this.owner;
        let lastStartInfo = undefined as RotatorStartInfoType | undefined;
        this.dispose(() => this.owner.hoveredPlaneType = 'none');

        const process = this._createRotatorMoveProcessing();
        // 鼠标移动判断是否悬浮在某个平面上，进而高亮
        this.dispose(czmViewer.pointerMoveEvent.disposableOn(pointerEvent => {
            const startInfo = lastStartInfo = this._getStartInfo(pointerEvent.pointerEvent as PointerEvent);
            if (!startInfo) return;
            this.owner.hoveredPlaneType = startInfo.currentPlaneType;
        }));
        this.dispose(czmViewer.pointerDownEvent.disposableOn(pointerEvent => {
            if (!lastStartInfo) return;
            if (lastStartInfo.currentPlaneType === 'none') return;
            process.restart(undefined, czmViewer, lastStartInfo);
            lastStartInfo = undefined;
        }));
        // 双击轴，恢复0度，双击空白处，关闭编辑
        this.dispose(czmViewer.dblclickEvent.disposableOn(pointerEvent => {
            do {
                const startInfo = this._getStartInfo(pointerEvent.pointerEvent as PointerEvent);
                if (!startInfo) break;
                if (startInfo.currentPlaneType === 'none') break;
                const { currentPlaneType } = startInfo;
                const rotation = [...this.owner.selfRotation] as [number, number, number];
                rotation[RotationEditing.rotationNum[currentPlaneType]] = 0;
                this.owner.selfRotation = rotation;
                return;
            } while (false);
            this.owner.enabled = false;
        }));
    }
    private _createRotatorMoveProcessing() {
        return this.ad(createProcessingFromAsyncFunc<void, [czmViewer: ESCesiumViewer, startInfo: RotatorStartInfoType]>(async (cancelsManager, czmViewer, startInfo) => {
            const circle = this.owner.circles[startInfo.currentPlaneType as Exclude<RotatorPlaneType, 'none'>]
            czmViewer.incrementDisabledInputStack();
            cancelsManager.disposer.dispose(() => czmViewer.decrementDisabledInputStack());
            cancelsManager.disposer.dispose(czmViewer.pointerMoveEvent.don(pointerEvent => {
                if (startInfo && pointerEvent.pointerEvent) {
                    const { offsetX, offsetY } = pointerEvent.pointerEvent;
                    const windowPos = [offsetX, offsetY] as [number, number];

                    const { currentPlaneType } = startInfo;
                    if (currentPlaneType === 'none') return;

                    const plane = this.owner.getPlane(currentPlaneType);
                    if (!startInfo.currentV) return;
                    const v = plane.pick(windowPos);
                    if (!v) return;
                    if (!this.owner.cartesian) return;
                    const startDir = Cesium.Cartesian3.subtract(startInfo.currentV, this.owner.cartesian, new Cesium.Cartesian3());
                    Cesium.Cartesian3.normalize(startDir, startDir);
                    const stopDir = Cesium.Cartesian3.subtract(v, this.owner.cartesian, new Cesium.Cartesian3());
                    Cesium.Cartesian3.normalize(stopDir, stopDir);
                    if (!plane.normal || !plane.origin) return;
                    const angle = computeAngle(startDir, stopDir, plane.normal);
                    const startAngle = computeAngle(plane.origin, startDir, plane.normal);
                    const stopAngle = computeAngle(plane.origin, stopDir, plane.normal);
                    {
                        const r = [...this.owner.selfRotation] as [number, number, number];
                        r[RotationEditing.rotationNum[currentPlaneType]] = startInfo.startRotation + angle;
                        this.owner.selfRotation = r;
                        circle.circleStartRotation = startAngle;
                        circle.circleEndRotation = stopAngle;
                    }
                }
            }))
            this.owner.movingPlaneType = startInfo.currentPlaneType;
            cancelsManager.disposer.dispose(() => {
                this.owner.movingPlaneType = 'none'
            })
            cancelsManager.disposer.dispose(() => {
                circle.circleStartRotation = 0;
                circle.circleEndRotation = 0;
            })
            await step(cancelsManager, cancelsManager => {
                return new Promise<void>((resolve, reject) => {
                    cancelsManager.disposer.dispose(reject);
                    cancelsManager.disposer.dispose(czmViewer.pointerUpEvent.don(() => { resolve() }));
                    cancelsManager.disposer.dispose(czmViewer.pointerOutEvent.don(() => { resolve() }));
                })
            })
        }))
    }
    private _pickPlane(
        startInfo: RotatorStartInfoType,
        scene: Cesium.Scene,
        type: Exclude<RotatorPlaneType, 'none'>,
        windowPos: [number, number],
        sceneScale: number,
    ) {
        const rotatorCartesian = this.owner.cartesian;
        if (!rotatorCartesian) return;

        const { selfRotation } = this.owner;

        const v = this.owner.getPlane(type).pick(windowPos);
        if (!v) return;
        const d = Cesium.Cartesian3.distance(rotatorCartesian, v);

        const baseDistance = sceneScale / 4;
        const halfRange = baseDistance * 3 / 128;
        const inRange = d >= baseDistance - halfRange && d <= baseDistance + halfRange;
        if (!inRange) return;
        const d2c = Cesium.Cartesian3.distance(scene.camera.positionWC, v);
        if (startInfo.currentD2c <= d2c) return;
        startInfo.currentPlaneType = type;
        startInfo.currentD2c = d2c;
        startInfo.currentV = v;
        startInfo.startRotation = selfRotation[RotationEditing.rotationNum[type]];
    };
    private _getStartInfo(pointerEvent: PointerEvent) {
        const { czmViewer } = this.owner;
        const scene = czmViewer.viewer?.scene as Cesium.Scene;
        if (!this.owner.position) return;
        if (!this.owner.cartesian) return;
        const sceneScale = getSceneScaleForScreenPixelSize(scene, this.owner.cartesian, this.owner.pixelSize);
        if (!sceneScale) return;

        const { offsetX, offsetY } = pointerEvent;
        const windowPos = [offsetX, offsetY] as [number, number];
        const pointerId = pointerEvent.pointerId;
        const startInfo: RotatorStartInfoType = {
            currentPlaneType: 'none',
            currentD2c: Number.POSITIVE_INFINITY,
            currentV: undefined,
            startRotation: 0,
            pointerId,
        };

        this._pickPlane(startInfo, scene, 'heading', windowPos, sceneScale);
        this._pickPlane(startInfo, scene, 'pitch', windowPos, sceneScale);
        this._pickPlane(startInfo, scene, 'roll', windowPos, sceneScale);

        return startInfo;
    }
}
function computeAngle(startDir: Cesium.Cartesian3, stopDir: Cesium.Cartesian3, normalDir: Cesium.Cartesian3) {
    const testNormalDir = Cesium.Cartesian3.cross(stopDir, startDir, new Cesium.Cartesian3());
    if (testNormalDir.equals(Cesium.Cartesian3.ZERO)) return 0;
    Cesium.Cartesian3.normalize(testNormalDir, testNormalDir);
    const dot = Cesium.Cartesian3.dot(testNormalDir, normalDir);
    let angle = Math.acos(Cesium.Cartesian3.dot(startDir, stopDir)) * 180 / Math.PI;
    angle = dot > 0 ? angle : -angle; // 弧度
    return angle;
}